package com.alili_tv.system.controller;

import com.alili_tv.system.common.BaseContext;
import com.alili_tv.system.common.LoginRequired;
import com.alili_tv.system.common.R;
import com.alili_tv.system.common.RedisRank;
import com.alili_tv.system.entity.*;
import com.alili_tv.system.exception.BusinessException;
import com.alili_tv.system.exception.SystemException;
import com.alili_tv.system.service.*;
import com.alili_tv.system.utils.IpUtils;
import com.alili_tv.system.utils.VideoUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 视频/合集表 前端控制器
 * </p>
 *
 * @author baomidou
 * @since 2023-03-17
 */
@RestController
@RequestMapping(value = "/video", produces = "application/json")
public class VideoController {

    @Resource
    private VideoService videoService;

    @Resource
    private VideoRankService videoRankService;

    @Resource
    private UserService userService;

    @Resource
    private FollowService followService;

    @Resource
    private LikesService likesService;

    @Resource
    private BulletService bulletService;

    @Resource
    private CommentService commentService;

    @Resource
    private TagService tagService;

    @Resource
    private VideoTagService videoTagService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private RedisRank redisRank;

    @Resource
    private CollectFolderService collectFolderService;


    /**
     * 通过id获取视频信息
     *
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public R<Video> getById(@PathVariable Long id, HttpServletRequest request) {
        Video videoInfo = videoService.getById(id);

        Long userId = BaseContext.getCurrentId();

        if (videoInfo != null) {
            boolean isFollow = false;
            if (userId != null) {
                // 判断用户是否点赞
                boolean isLike = likesService.isLikeOfVideo(userId, id);
                // 判断用户是否收藏
                boolean isCollect = collectFolderService.isCollect(id, userId);
                // 判断用户是否已关注
                isFollow = followService.isFollow(userId, videoInfo.getCreateUser());

                videoInfo.setIsLike(isLike);
                videoInfo.setIsCollect(isCollect);
            } else {
                videoInfo.setIsLike(false);
                videoInfo.setIsCollect(false);
            }

            // 查询视频的作者信息, 粉丝数, 弹幕数、点赞数、收藏数和评论数
            User uploader = userService.getById(videoInfo.getCreateUser());
            long fansCount = userService.getFansCount(videoInfo.getCreateUser());
            uploader.setFansCount(fansCount);
            uploader.setIsFollow(isFollow);
            videoInfo.setUser(uploader);
            videoInfo.setBarrageCount(bulletService.countByVideoId(id));
            videoInfo.setLikeCount(likesService.countByVideoId(id));
            videoInfo.setCollectCount(collectFolderService.countByVideoId(id));
            videoInfo.setCommentCount(commentService.getCountByVideoId(id));

            // 添加视频播放
            ValueOperations<String, Object> ops = redisTemplate.opsForValue();
            String userIp = IpUtils.getIpAddr(request);
            String key = userIp + id;
            // 若5分钟内已经观看了视频, 则不会增加播放量
            if (!Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
                LambdaUpdateWrapper<Video> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper
                        .setSql("view_count = view_count + 1")
                        .eq(Video::getId, id);
                videoService.update(updateWrapper);

                ops.set(key, "", 5, TimeUnit.MINUTES);

                // 增加热度
                // 若观看者不是作者的粉丝, 则说明这个视频可能更有吸引力, 热度应该设置得更高
                if (isFollow) {
                    redisRank.incrScore(id, 5);
                } else {
                    redisRank.incrScore(id, 6);
                }

            }

            return R.success(videoInfo);
        }

        return R.error("视频不见了");
    }

    /**
     * 保存视频信息
     *
     * @param video
     * @return
     * @throws IOException
     */
    @LoginRequired
    @PostMapping
    @Transactional
    public R<Video> save(@RequestBody Video video) throws IOException {
        // 获取缓存的文件名
        String keyV = BaseContext.getCurrentId() + "_video";
        String keyC = BaseContext.getCurrentId() + "_cover";
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();

        String videoName = (String) ops.get(keyV);
        String coverName = (String) ops.get(keyC);
        video.setVideoPath(videoName);
        video.setCoverPath(coverName);

        // 获取视频文件的时长
        String filePath = CommonController.videoPath + videoName;
        long duration = VideoUtils.getDuration(filePath);
        video.setTotalTime((int) duration);

        // 保存视频信息 (获取生成的视频id)
        videoService.save(video);

        // 保存tag信息
        tagService.mySaveOrUpdateBatch(video.getTags(), video.getChannel());

        // 获取视频tag的id
        List<String> nameList = video.getTags().stream().map(Tag::getName).toList();
        List<Tag> tagsId = tagService.getIdByChannelAndName(video.getChannel(), nameList);

        // 保存视频tag关联信息
        List<VideoTag> videoTags = tagsId.stream().map((item) -> {
            VideoTag videoTag = new VideoTag();
            videoTag.setTagId(item.getId());
            videoTag.setVideoId(video.getId());
            return videoTag;
        }).toList();
        videoTagService.saveBatch(videoTags);

        // 删除缓存的文件名
        redisTemplate.delete(keyV);
        redisTemplate.delete(keyC);
        return R.success(video);
    }

    /**
     * 修改视频信息
     * @param video
     * @return
     * @throws IOException
     */
    @LoginRequired
    @PutMapping
    public R<String> update(@RequestBody Video video) throws IOException {
        if (video == null || video.getId() == null) {
            return R.error("修改信息错误");
        }
        Long userId = BaseContext.getCurrentId();
        // 判断此视频是否是房前用户发布的
        Video selectVideo = videoService.getById(video.getId());
        if (!Objects.equals(selectVideo.getCreateUser(), userId)) {
            return R.error("您没有权限修改此视频");
        }

        // 若更改了视频源，则更新视频时长
        if (video.getVideoPath() != null && !Objects.equals(selectVideo.getVideoPath(), video.getVideoPath())) {
            String filePath = CommonController.videoPath + video.getVideoPath();
            long duration = VideoUtils.getDuration(filePath);
            video.setTotalTime((int) duration);
        }

        // 以下是不可更改的内容
        video.setCreateTime(null);
        video.setViewCount(null);

        // 更改视频信息
        videoService.updateById(video);

        new Thread(() -> {
            BaseContext.setCurrentId(userId);
            // 保存tag信息
            tagService.mySaveOrUpdateBatch(video.getTags(), video.getChannel());

            // 获取视频tag的id
            List<String> nameList = video.getTags().stream().map(Tag::getName).toList();
            List<Tag> tagIds = tagService.getIdByChannelAndName(video.getChannel(), nameList);

            // 删除视频tag
            LambdaQueryWrapper<VideoTag> vtQW = new LambdaQueryWrapper<VideoTag>()
                    .eq(VideoTag::getVideoId, video.getId());
            videoTagService.remove(vtQW);
            // 更新视频tag关联信息
            List<VideoTag> videoTags = tagIds.stream().map((item) -> {
                VideoTag videoTag = new VideoTag();
                videoTag.setTagId(item.getId());
                videoTag.setVideoId(video.getId());
                return videoTag;
            }).toList();
            videoTagService.saveBatch(videoTags);
        }).start();

        return R.success("修改成功");

    }

    /**
     * 获取推荐视频
     *
     * @param count
     * @param channelId 为null时，获取首页推荐
     * @return
     */
    @GetMapping("/getRecommend")
    public R<List<Video>> getRecommend(Integer count, Integer channelId) {
        if (count == null) {
            throw new BusinessException("请求失败");
        }
        List<Video> recommends = videoService.getRecommend(count, channelId);
        return R.success(recommends);
    }


    /**
     * 获取排行榜信息
     *
     * @param type      daily week month
     * @param count     需要获取的数量
     * @param channelId 若不为null, 则表示查询指定分区下的排行榜数据, 否则查询总榜
     * @return
     */
    @GetMapping("/getRank/{type}/{count}")
    public R<List<Video>> getRank(@PathVariable String type, @PathVariable Integer count, Integer channelId) {
        // 先获取排行榜缓存数据
        Map<Long, Double> rank;
        if (channelId == null) {
            rank = switch (type) {
                case "daily" -> redisRank.getRankDaily(count);
                case "week" -> redisRank.getRankWeek(count);
                case "month" -> redisRank.getRankMonth(count);
                default -> throw new BusinessException("系统异常");
            };
        } else {
            rank = switch (type) {
                case "daily" -> redisRank.getRankDaily(count, channelId);
                case "week" -> redisRank.getRankWeek(count, channelId);
                case "month" -> redisRank.getRankMonth(count, channelId);
                default -> throw new BusinessException("系统异常");
            };
        }

        if (rank == null || rank.isEmpty()) {
            return R.success(new ArrayList<>());
        }

        // 收集视频id
        Set<Long> ids = rank.keySet();

        // 查询视频信息
        List<Video> videos = videoService.getOrderByIn(ids);
        // 收集uploader的id
        Set<Long> userIds = videos.stream().map(Video::getCreateUser).collect(Collectors.toSet());
        // 查询uploader信息
        Map<Long, User> userMap = userService.getByIds(userIds);
        videos.forEach(item -> {
            item.setUser(userMap.get(item.getCreateUser()));
            item.setScore(rank.get(item.getId()));
        });

        return R.success(videos);
    }

    /**
     * 分页 用户动态
     *
     * @param pageIndex
     * @param pageSize
     * @return
     */
    @LoginRequired
    @GetMapping("/getDynamic/{pageIndex}/{pageSize}")
    public R<List<Video>> getDynamic(@PathVariable Integer pageIndex, @PathVariable Integer pageSize) {
        List<Video> list = videoService.getDynamic(BaseContext.getCurrentId(), pageIndex, pageSize);
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .set(User::getDynamic, LocalDateTime.now())
                .eq(User::getId, BaseContext.getCurrentId());
        userService.update(updateWrapper);
        return R.success(list);
    }

    /**
     * 根据用户id获取该用户发布的视频信息列表
     * @param userId
     * @param pageIndex
     * @param pageSize
     * @param channelId 0：全部分区
     * @param sortType  1：最新，2：最多播放
     * @return
     */
    @GetMapping("/getByUserId")
    public R<Page<Video>> getByUserId(Long userId, Integer pageIndex, Integer pageSize, Integer sortType,
                                      @RequestParam(required = false) Integer channelId) {
        Page<Video> page = new Page<>(pageIndex, pageSize);

        LambdaQueryWrapper<Video> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Video::getCreateUser, userId)
                .eq(channelId != null && channelId != 0, Video::getChannel, channelId)
                .orderByDesc(sortType == 1, Video::getCreateTime)
                .orderByDesc(sortType == 2, Video::getViewCount);

        videoService.page(page, queryWrapper);

        return R.success(page);
    }


    /**
     * 获取自己发布的视频id和title，用于生成下拉框信息
     *
     * @return
     */
    @LoginRequired
    @GetMapping("/getIdTitle")
    public R<List<Video>> getIdTitle() {
        LambdaQueryWrapper<Video> queryWrapper = new LambdaQueryWrapper<Video>()
                .select(Video::getId, Video::getTitle)
                .eq(Video::getCreateUser, BaseContext.getCurrentId())
                .orderByDesc(Video::getCreateTime);
        List<Video> videoList = videoService.list(queryWrapper);
        return R.success(videoList);
    }

    @LoginRequired
    @Transactional
    @DeleteMapping("/{videoId}")
    public R<String> remove(@PathVariable Long videoId) {
        if (videoId == null) {
            return R.error("请选择要删除的视频");
        }
        // 判断此视频是否是当前用户发布的
        Long userId = BaseContext.getCurrentId();
        Video video = videoService.getById(videoId);
        if (video == null || !Objects.equals(video.getCreateUser(), userId)) {
            return R.error("您没有权限删除此视频");
        }

        // 删除视频
        videoService.removeById(videoId);

        new Thread(() -> {
            // 获取评论ids、删除所有评论
            LambdaQueryWrapper<Comment> cmtQW = new LambdaQueryWrapper<Comment>()
                    .eq(Comment::getVideoId, videoId);
            List<Long> cmtIds = commentService.list(cmtQW).stream().map(Comment::getId).toList();
            List<Long> cvIds = new ArrayList<>(cmtIds);
            commentService.remove(cmtQW);
            // 删除弹幕信息
            LambdaQueryWrapper<Bullet> bltQW = new LambdaQueryWrapper<Bullet>()
                    .eq(Bullet::getVideoId, videoId);
            bulletService.remove(bltQW);
            // 删除视频和评论的点赞信息
            cvIds.add(videoId);
            LambdaQueryWrapper<Likes> likeQW = new LambdaQueryWrapper<Likes>()
                    .in(Likes::getCvId, cvIds);
            likesService.remove(likeQW);
            // 删除排行榜
            LambdaQueryWrapper<VideoRank> vrQW = new LambdaQueryWrapper<VideoRank>()
                    .eq(VideoRank::getVideoId, videoId);
            videoRankService.remove(vrQW);
        }).start();
        return R.success("删除成功");
    }
}
